/*  This file is part of the comicconquer project
    Copyright (C) 2010 Reach <reach334@gmail.com>

    ComicConquer is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This software is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public 
    License along with this software.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "MainDialog.h"
#include "onyx/ui/number_dialog.h"
#include "FileBrowser.h"
#include "StatusDlg.h"
#include "GrayScaleDlg.h"
#include "BookmarkDlg.h"
#include "ZipContainer.h"

#include <QtGui/QtGui>

CoListItems g_mainOperations;
QDialog* g_mainDialog;

/**
 * Constructor.
 * 
 * @author reach (5/26/2010)
 * 
 * @param parent: Widget parent.
 */
MainDialog::MainDialog(QWidget* parent)
: QDialog(parent),
m_startScreenDegree(sys::SysStatus::instance().screenTransformation())
{
    // Set the current path to that the executable file is in.
    QDir::setCurrent(get_exe_path());

    Open_Log(); 
    In_Log(__FUNCTION__, COCLASSNAME);

    g_mainDialog = this;

    // Attatch MouseConroler
    CO_MOUSEATTATCH;

    // Initialize the names and id list of main operations.
    setOperations();

    // Initialize UI.
    initUI();

    // Processing thread.
    m_thread = new ProcessThread(this);

    // Profile.
    m_profile = new CoconProfile();

    // DisplayControler.
    m_displayControler = new DisplayControler(m_profile, this);

    // Setting dialog.
    m_settingDlg = new SettingDlg(m_profile);

    // Connect signals.
    connectSignals();

    // Set m_timerUpdate to a single-shot timer.
    m_timerUpdate.setSingleShot(true);

    // Update waveform according to the flush mode of profile.
    updateWaveform();

    // Open last reading comic, or popup the file browser if cann't open.
    if (m_profile->iPage == -1 || !openComic(m_profile->filePath))
    {
        fileBrowse();
    }

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Destructor, release resources.
 * 
 * @author reach (5/26/2010)
 */
MainDialog::~MainDialog()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    if (!m_displayControler->isEmpty())
    {
        m_profile->iPage = m_displayControler->getPage()->iPage;
        m_profile->iDivPage = m_displayControler->getPage()->iDivPage;
        m_profile->writeProfile();
    }

    if (m_profile != NULL)
    {
        delete m_profile;
        m_profile = NULL;
    }

    if (m_thread != NULL)
    {
        delete m_thread;
        m_thread = NULL;
    }

    sys::SysStatus::instance().setScreenTransformation(m_startScreenDegree);

    Out_Log(__FUNCTION__, COCLASSNAME);
    Close_Log();
}

/**
 * Initialize the names and id list of main operations.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::setOperations()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    g_mainOperations.append(CoListItem(tr("不使用"), Oper_Invalid));
    g_mainOperations.append(CoListItem(tr("上一页"), Oper_PrevPage));
    g_mainOperations.append(CoListItem(tr("下一页"), Oper_NextPage));
    g_mainOperations.append(CoListItem(tr("退出"), Oper_Close));
    g_mainOperations.append(CoListItem(tr("灰度调节"), Oper_Grayscale));
    g_mainOperations.append(CoListItem(tr("设置菜单"), Oper_Menu));
    g_mainOperations.append(CoListItem(tr("状态信息"), Oper_StatusDlg));
    g_mainOperations.append(CoListItem(tr("打开文件"), Oper_FileBrowser));
    g_mainOperations.append(CoListItem(tr("切换原图"), Oper_Origin));
    g_mainOperations.append(CoListItem(tr("页面跳转"), Oper_GotoPage));
    g_mainOperations.append(CoListItem(tr("向上滚屏"), Oper_ScrollUp));
    g_mainOperations.append(CoListItem(tr("向下滚屏"), Oper_ScrollDown));
    g_mainOperations.append(CoListItem(tr("向左滚屏"), Oper_ScrollLeft));
    g_mainOperations.append(CoListItem(tr("向右滚屏"), Oper_ScrollRight));
    g_mainOperations.append(CoListItem(tr("向上滚动"), Oper_MoveUp));
    g_mainOperations.append(CoListItem(tr("向下滚动"), Oper_MoveDown));
    g_mainOperations.append(CoListItem(tr("向左滚动"), Oper_MoveLeft));
    g_mainOperations.append(CoListItem(tr("向右滚动"), Oper_MoveRight));
    g_mainOperations.append(CoListItem(tr("拖动图片"), Oper_Drag));
    g_mainOperations.append(CoListItem(tr("刷新屏幕"), Oper_Refresh));
    g_mainOperations.append(CoListItem(tr("上一漫画"), Oper_PrevComic));
    g_mainOperations.append(CoListItem(tr("下一漫画"), Oper_NextComic));
    g_mainOperations.append(CoListItem(tr("添加书签"), Oper_AddBookmark));
    g_mainOperations.append(CoListItem(tr("删除书签"), Oper_RemoveBookmark));
    g_mainOperations.append(CoListItem(tr("进入书签"), Oper_ManageBookmark));

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Initialize UI.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::initUI()
{
    In_Log(__FUNCTION__, COCLASSNAME);

//#ifndef QT_ARCH_ARM
#if 0
    // UI initialize
    QWidget* w = new QWidget(this); // use to move the layout
    w->move(0, rect().bottom() - 20);
    m_hlayout = new QHBoxLayout(w);
    m_bScale = new QPushButton("&Scale");
    m_hlayout->addWidget(m_bScale);
    m_bThresh = new QPushButton("&Thresh");
    m_hlayout->addWidget(m_bThresh);
    m_bContour = new QPushButton("&Contour");
    m_hlayout->addWidget(m_bContour);
    m_bLine = new QPushButton("&Line");
    m_hlayout->addWidget(m_bLine);
    m_bScroll = new QPushButton("&Scroll");
    m_hlayout->addWidget(m_bScroll);
#endif

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Connect signals.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::connectSignals()
{
    //-- Signals for UI.
//#ifndef QT_ARCH_ARM
#if 0
    connect(m_bScale, SIGNAL(clicked()), this, SLOT(OnScale()));
    connect(m_bThresh, SIGNAL(clicked()), this, SLOT(OnThresh()));
    connect(m_bContour, SIGNAL(clicked()), this, SLOT(OnContour()));
    connect(m_bLine, SIGNAL(clicked()), this, SLOT(OnLine()));
    connect(m_bScroll, SIGNAL(clicked()), this, SLOT(OnScroll()));
#endif

    //-- Signals for system to shutdown and wakeup the machine.
    connect(&sys::SysStatus::instance(), SIGNAL(aboutToShutdown()), this, SLOT(shutdown()));
    connect(&sys::SysStatus::instance(), SIGNAL(wakeup()), this, SLOT(wakeup()));

    //-- Signals for timer to delay update.
    connect(&m_timerUpdate, SIGNAL(timeout()), this, SLOT(update()));

    //-- Signals for processing thread to send a processed page.
    connect(m_thread, SIGNAL(SendPage(DivPage*)), this, SLOT(updatePage(DivPage*)));

    //-- Signals for display control.
    connect(m_displayControler, SIGNAL(prevPage()), this, SLOT(requestPrevPage()), Qt::QueuedConnection);
    connect(m_displayControler, SIGNAL(nextPage()), this, SLOT(requestNextPage()), Qt::QueuedConnection);
    connect(m_displayControler, SIGNAL(prevComic()), this, SLOT(prevComic()), Qt::QueuedConnection);
    connect(m_displayControler, SIGNAL(nextComic()), this, SLOT(nextComic()), Qt::QueuedConnection);
    connect(m_displayControler, SIGNAL(requestPage(int, int, bool)),
            this, SLOT(requestPage(int, int, bool)));
}

/**
 * Reflesh screen of OnyxBoox.
 * 
 * @author reach (5/26/2010)
 * 
 * @param w: Flush waveform.
 */
void MainDialog::refreshScreen(ScreenProxy::Waveform w)
{
    if (w == ScreenProxy::INVALID)
    {
        w = m_waveForm;
        if (w == ScreenProxy::GU)
        {
            if (m_profile->iClearInterval && !(m_flushCount++ % m_profile->iClearInterval))
            {
                w = ScreenProxy::GC;
            }
        }
    }

    ScreenProxy::instance().flush(this, w);
}

/**
 * Update waveform according to the flush mode of profile.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::updateWaveform()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    m_flushCount = 0;
    if (m_profile->iFlushMode)
    {
        m_waveForm = ScreenProxy::GU; 
    }
    else
    {
        m_waveForm = ScreenProxy::GC; 
    }

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Popup the file browser to open a comic.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::fileBrowse()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    QDir dir;

    if (!m_profile->isZip)
    {
        dir.setPath(m_profile->filePath);
        dir.cdUp();
    }
    else
    {
        dir.setPath(m_profile->filePath.section('/', 0, -2, QString::SectionIncludeLeadingSep));
    }

    QString folderName = FileBrowser::fileBrowse(dir.absolutePath(), this);

    openComic(folderName);

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Open the previous comic.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::prevComic()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    //-- Get the parent dierctory of the current comic.
    QDir upDir(m_dir); 

    if (!m_profile->isZip)
    {
        upDir.cdUp();
    }

    //-- Get the directory and zip items.
    QStringList dirNames = upDir.entryList(QStringList()<<"*.zip");
    dirNames += upDir.entryList(QDir::Dirs).mid(2);
    QString curDirName = m_profile->filePath.section('/', -1, -1);

    //-- Iterate the items to find the previous comic.
    int i = dirNames.indexOf(curDirName);

    for (i = i - 1; i >= 0; i--)
    {
        // Is zip file.
        if (dirNames[i].endsWith(".zip", Qt::CaseInsensitive))
        {
            ui::MessageDialog queDlg(QMessageBox::Question,
                                     tr("询问"), tr("上一本漫画为“") + dirNames[i] + tr("”，是否打开？"),
                                     QMessageBox::Yes | QMessageBox::No);

            if (queDlg.exec() == QMessageBox::Yes)
            {
                openComic(upDir.absoluteFilePath(dirNames[i]));
            }
            else
            {
                update();
            }

            Out_Log(__FUNCTION__, COCLASSNAME);
            return;
        }
        // Is directory.
        else
        {
            QDir dir;
            dir.setPath(upDir.absoluteFilePath(dirNames[i]));
            QStringList fileNames = dir.entryList(QStringList()<<"*.jpg"<<"*.png",
                                                  QDir::NoFilter, QDir::Name | QDir::IgnoreCase);

            if (!fileNames.empty())
            {
                ui::MessageDialog queDlg(QMessageBox::Question,
                                         tr("询问"), tr("上一本漫画为“") + dirNames[i] + tr("”，是否打开？"),
                                         QMessageBox::Yes | QMessageBox::No);

                if (queDlg.exec() == QMessageBox::Yes)
                {
                    openComic(dir.absolutePath()); 
                }
                else
                {
                    update();
                }

                Out_Log(__FUNCTION__, COCLASSNAME);
                return;
            }
        }
    }

    ui::MessageDialog infoDlg(QMessageBox::Information,
                              tr("提示"), tr("已是第一本漫画"),
                              QMessageBox::Ok);

    infoDlg.exec();
    update();

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Open the next comic.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::nextComic()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    //-- Get the parent dierctory of the current comic.
    QDir upDir(m_dir); 

    if (!m_profile->isZip)
    {
        upDir.cdUp();
    }

    //-- Get the directory and zip items.
    QStringList dirNames = upDir.entryList(QStringList()<<"*.zip");
    dirNames += upDir.entryList(QDir::Dirs).mid(2);
    QString curDirName = m_profile->filePath.section('/', -1, -1);

    //-- Iterate the items to find the previous comic.
    int i = dirNames.indexOf(curDirName);

    for (i = i + 1; i < dirNames.count(); i++)
    {
        // Is zip file.
        if (dirNames[i].endsWith(".zip", Qt::CaseInsensitive))
        {
            ui::MessageDialog queDlg(QMessageBox::Question,
                                     tr("询问"), tr("下一本漫画为“") + dirNames[i] + tr("”，是否打开？"),
                                     QMessageBox::Yes | QMessageBox::No);

            if (queDlg.exec() == QMessageBox::Yes)
            {
                openComic(upDir.absoluteFilePath(dirNames[i]));
            }
            else
            {
                update();
            }

            Out_Log(__FUNCTION__, COCLASSNAME);
            return;
        }
        // Is directory.
        else
        {
            QDir dir;
            dir.setPath(upDir.absoluteFilePath(dirNames[i]));
            QStringList fileNames = dir.entryList(QStringList()<<"*.jpg"<<"*.png",
                                                  QDir::NoFilter, QDir::Name | QDir::IgnoreCase);

            if (!fileNames.empty())
            {
                ui::MessageDialog queDlg(QMessageBox::Question,
                                         tr("询问"), tr("下一本漫画为“") + dirNames[i] + tr("”，是否打开？"),
                                         QMessageBox::Yes | QMessageBox::No);

                if (queDlg.exec() == QMessageBox::Yes)
                {
                    openComic(dir.absolutePath()); 
                }
                else
                {
                    update();
                }

                Out_Log(__FUNCTION__, COCLASSNAME);
                return;
            }
        }
    }

    ui::MessageDialog infoDlg(QMessageBox::Information,
                              tr("提示"), tr("已是最后一本漫画"),
                              QMessageBox::Ok);

    infoDlg.exec();
    update();

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Open a comic.
 * 
 * @author reach (5/26/2010)
 * 
 * @param filename: The path name of comic.
 * 
 * @return bool: True for successfully opening.
 */
bool MainDialog::openComic(const QString& filename)
{
    In_Log(__FUNCTION__, COCLASSNAME);

    //-- If there is comic opened, save the configurations.
    if (!m_displayControler->isEmpty())
    {
        m_profile->iPage = m_displayControler->getPage()->iPage;
        m_profile->iDivPage = m_displayControler->getPage()->iDivPage;
        m_profile->writeLocalConfig();
        m_profile->writeConfig();
    }

    //-- Return if the filename is invalid or the same of the current comic.
    if (filename.isEmpty() || (!m_displayControler->isEmpty() && filename == m_profile->filePath))
    {
        update();

        Out_Log(__FUNCTION__, COCLASSNAME);
        return false; 
    }

    ZipContainer* zipContainer;

    //-- Is zip file.
    if (filename.endsWith(".zip", Qt::CaseInsensitive))
    {
        // Construct a zipContainer of this filename.
        zipContainer = new ZipContainer(filename);

        // The zip file do not contain image file.
        if (zipContainer->isEmpty())
        {
            delete zipContainer;
            ui::MessageDialog errorDlg(QMessageBox::Information,
                                       tr("错误"), tr("zip文件不能打开或没有包含支持的图片文件"),
                                       QMessageBox::Ok);

            errorDlg.exec();
            update();

            Out_Log(__FUNCTION__, COCLASSNAME);
            return false;
        }

        m_dir.setPath(filename.section('/', 0, -2, QString::SectionIncludeLeadingSep));
        m_profile->isZip = true;
        m_profile->iPage = 0;
        m_profile->iDivPage = 0;
    }
    // Is directory.
    else
    {
        QDir dir;
        dir.setPath(filename);
        QStringList fileNames = dir.entryList(QStringList()<<"*.jpg"<<"*.png", QDir::NoFilter, QDir::Name | QDir::IgnoreCase);

        // There is no image file in this directory.
        if (fileNames.empty())
        {
            ui::MessageDialog errorDlg(QMessageBox::Information,
                                       tr("错误"), tr("该路径不可访问"),
                                       QMessageBox::Ok);

            errorDlg.exec();
            update();
            Out_Log(__FUNCTION__, COCLASSNAME);
            return false; 
        }

        m_dir = dir;
        m_sFileNames = fileNames;
        m_profile->isZip = false;
        m_profile->iPage = 0;
        m_profile->iDivPage = 0;
    }

    m_isPrev = false;

    // initialize members
    m_profile->filePath = filename;
    m_profile->readLocalConfig();

    updateWaveform();

    bool sideWards = m_profile->imgProfile.option & Opt_SideWards;
    if (m_profile->iSideWardsDirect == 0) // left-hand
    {
        sys::SysStatus::instance().setScreenTransformation(90 * sideWards);
    }
    else // right-hand
    {
        sys::SysStatus::instance().setScreenTransformation(270 * sideWards);
    }

    GrayScaleDlg::setGrayTable(m_colorTable,
                               m_profile->grayscale.black,
                               m_profile->grayscale.middle,
                               m_profile->grayscale.white);

    if (m_profile->iPage == -1)
    {
        m_profile->iPage = 0;
    }

    m_displayControler->clearPages();

    update();

    if (m_profile->isZip)
    {
        m_thread->LoadZip(zipContainer, m_profile->imgProfile, m_profile->iPage, m_profile->iDivPage);
    }
    else
    {
        m_thread->LoadDir(m_dir, m_sFileNames, m_profile->imgProfile, m_profile->iPage, m_profile->iDivPage);
    }

    m_profile->writeConfig();
    m_profile->writeLocalConfig();

    Out_Log(__FUNCTION__, COCLASSNAME);
    return true;
}

/**
 * Handle the paint event, let DisplayControler to paint the comic pages.
 * 
 * @author reach (5/26/2010)
 * 
 * @param e: Event send by the QApplicaton.
 */
void MainDialog::paintEvent(QPaintEvent* e)
{
    In_Log(__FUNCTION__, COCLASSNAME);

    QPainter painter(this);

    m_displayControler->paint(painter, m_colorTable);

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Handle update request event.
 * 
 * @author reach (5/25/2010)
 * 
 * @param event: The event from QApplication.
 * 
 * @return bool
 */
bool MainDialog::event(QEvent* event)
{
    bool ret = QDialog::event(event);

    // Switch the event type
    switch (event->type())
    {
    case QEvent::UpdateRequest:{
            refreshScreen();
            return true;
        }
    default:
        {

        }
        break;
    }   // end switch
    return ret;
}

/**
 * Handle global keyboard event. The event filter is setup to QApplycation.
 * 
 * @author reach (5/26/2010)
 * 
 * @param obj: 
 * @param evt: 
 * 
 * @return bool: 
 */
bool MainDialog::eventFilter(QObject* obj, QEvent* evt)
{
    if (evt->type() == QEvent::KeyPress || evt->type() == QEvent::KeyRelease)
    {
        QKeyEvent* keyEvent = static_cast<QKeyEvent*>(evt);

        // remap the key in state right-hand sidewards
        if (m_profile->imgProfile.option & Opt_SideWards && m_profile->iSideWardsDirect == 1)
        {
            qint32 key;
            switch (keyEvent->key())
            {
            case Qt::Key_Up:
                key = Qt::Key_Down;
                break;
            case Qt::Key_Down:
                key = Qt::Key_Up;
                break;
            case Qt::Key_Left:
                key = Qt::Key_Right;
                break;
            case Qt::Key_Right:
                key = Qt::Key_Left;
                break;

            default:
                key = keyEvent->key();
                break;
            }

            QKeyEvent keyEventToSend(keyEvent->type(), key, keyEvent->modifiers());
            *keyEvent = keyEventToSend;
        }

        if (QApplication::activeWindow() == m_settingDlg
            && keyEvent->key() != Qt::Key_Return
            && keyEvent->key() != Qt::Key_Escape
            && keyEvent->key() != Qt::Key_Backtab
            && keyEvent->key() != Qt::Key_Tab)
        {
            if (evt->type() == QEvent::KeyPress)
            {
                m_settingDlg->keyPressEvent(keyEvent);
            }

            return true;
        }
    }

    return false;
}

/**
 * Handle key press event send to the main dialog.
 * 
 * @author reach (5/26/2010)
 * 
 * @param event: 
 */
void MainDialog::keyPressEvent(QKeyEvent* event)
{
#ifndef QT_ARCH_ARM
    switch (event->key())
    {
    case Qt::Key_M:
        runOperation(Oper_Menu);
        break;
    case Qt::Key_S:
        {
            if (m_displayControler->getContent() != Cot_Scroll)
            {
                OnScroll();
            }
            else
            {
                m_displayControler->recoverContent();
                update();
            }
            return;
        }
        break;
    case Qt::Key_L:
        {
            if (m_displayControler->getContent() != Cot_Line)
            {
                OnLine();
            }
            else
            {
                m_displayControler->recoverContent();
                update();
            }
            return;
        }
        break;

    default:
        break;
    }
#endif

    runOperation(m_profile->keyMap[event->key()], event);
}

/**
 * Handle the event send by MouseConroler.
 * 
 * @author reach (5/26/2010)
 * 
 * @param event: 
 */
void MainDialog::mouseCtlEvent(GestureEvent* event)
{
    if (m_profile->gestureMap.contains(event->gesture))
    {
        runOperation(m_profile->gestureMap[event->gesture], NULL, event);
    }
}

/**
 * Run the operations requested by user.
 * 
 * @author reach (5/26/2010)
 * 
 * @param type: Operation type. See MainOperType.
 * @param keyEvent: The key event generate this operation.
 * @param mouseEvent: The mouse event generate this operation.
 */
void MainDialog::runOperation(int type, QKeyEvent* keyEvent, GestureEvent* mouseEvent)
{
    switch (type)
    {
    case Oper_Close:
        {
            QApplication::exit();
        }
        break;
    case Oper_Menu:
        {
            setting();
        }
        break;
    case Oper_PrevPage:
        {
            if (m_displayControler->isEmpty())
            {
                break; 
            }

            if (m_profile->imgProfile.option & Opt_ScrollMode)
            {
                m_displayControler->scrollUp();
            }
            else
            {
                requestPrevPage();
            }

            update();
        }
        break;
    case Oper_NextPage:
        {
            if (m_displayControler->isEmpty())
            {
                break; 
            }

            if (m_profile->imgProfile.option & Opt_ScrollMode)
            {
                m_displayControler->scrollDown();
            }
            else
            {
                requestNextPage();
            }

            update();
        }
        break;
    case Oper_ScrollUp:
        {
            m_displayControler->scrollUp();
            update();
        }
        break;
    case Oper_ScrollDown:
        {
            m_displayControler->scrollDown();
            update();
        }
        break;
    case Oper_ScrollLeft:
        {
            m_displayControler->scrollLeft();
            update();
        }
        break;
    case Oper_ScrollRight:
        {
            m_displayControler->scrollRight();
            update();
        }
        break;
    case Oper_MoveUp:
        {
            m_displayControler->moveUp();
            update();
        }
        break;
    case Oper_MoveDown:
        {
            m_displayControler->moveDown();
            update();
        }
        break;
    case Oper_MoveLeft:
        {
            m_displayControler->moveLeft();
            update();
        }
        break;
    case Oper_MoveRight:
        {
            m_displayControler->moveRight();
            update();
        }
        break;
    case Oper_GotoPage:
        {
            if (m_displayControler->isEmpty())
            {
                break; 
            }

            gotoPage();
            update();
        }
        break;
    case Oper_FileBrowser:
        {
            fileBrowse();
        }
        break;
    case Oper_StatusDlg:
        {
            int operation;
            if (!m_displayControler->isEmpty())
            {
                DivPage* page = m_displayControler->getPage();
                operation = StatusDlg::display(*m_profile, page->iPage, page->iDivPage, page->iPageCount, m_displayControler->getContent(), this);
            }
            else
            {
                operation = StatusDlg::display(*m_profile, 0, 0, 0, m_displayControler->getContent(), this);
            }
            update();
            runOperation(operation);
        }
        break;
    case Oper_Grayscale:
        {
            if (m_displayControler->isEmpty())
            {
                break; 
            }

            setGrayscale();
            m_timerUpdate.start(200);
        }
        break;
    case Oper_Drag:
        {
            m_displayControler->drag(-mouseEvent->offset);
            update();
        }
        break;
    case Oper_Origin:
        {
            if (m_displayControler->isEmpty())
            {
                break; 
            }

            if (mouseEvent != NULL)
            {
                m_displayControler->toggleOrigin(mouseEvent->clickPos); 
            }
            else
            {
                m_displayControler->toggleOrigin();
            }
            update();
        }
        break;
    case Oper_Refresh:
        {
            update();
        }
        break;
    case Oper_PrevComic:
        {
            prevComic();
        }
        break;
    case Oper_NextComic:
        {
            nextComic();
        }
        break;
    case Oper_AddBookmark:
        {
            m_profile->addBookmark();
            ui::MessageDialog Dlg(QMessageBox::Information,
                                  tr("提示"), tr("添加成功"),
                                  QMessageBox::Ok);

            Dlg.exec();
            update();
        }
        break;
    case Oper_RemoveBookmark:
        {
            BookmarkDlg::manageBookmark(m_profile, true, this);
            update();
        }
        break;
    case Oper_ManageBookmark:
        {
            BookmarkDlg::manageBookmark(m_profile, false, this);
            update();
        }
        break;

    default:
        break;
    }
}

/**
 * Request the previous page.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::requestPrevPage()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    DivPage* page = m_displayControler->getPage();

    if (page->isFirst())
    {
        prevComic();

        Out_Log(__FUNCTION__, COCLASSNAME);
        return;
    }

    int iPage = page->iPage;
    int iDivPage = page->iDivPage;

    // Need to turn the original page.
    if (--iDivPage < 0)
    {
        --iPage;
        iDivPage = 1;
    }

    m_displayControler->clearPages();

    requestPage(iPage, iDivPage, true);

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Request the next page.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::requestNextPage()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    DivPage* page = m_displayControler->getPage();

    if (page->isLast())
    {
        nextComic();

        Out_Log(__FUNCTION__, COCLASSNAME);
        return;
    }

    int iPage = page->iPage;
    int iDivPage = page->iDivPage;

    // Need to turn the original page.
    if (++iDivPage > page->iDivPageCount - 1)
    {
        ++iPage;
        iDivPage = 0;
    }

    m_displayControler->clearPages();

    requestPage(iPage, iDivPage);

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Request a page.
 * 
 * @author reach (5/26/2010)
 * 
 * @param iPage: The original page number.
 * @param iDivPage: The divide page number.
 * @param isPrev: If is request by requestPrevPage.
 */
void MainDialog::requestPage(int iPage, int iDivPage, bool isPrev)
{
    In_Log(__FUNCTION__, COCLASSNAME);

    m_isPrev = isPrev;

    DivPage* page = m_thread->RequestPage(iPage, iDivPage, isPrev);

    if (page != NULL)
    {
        m_displayControler->addPage(page, m_isPrev);
    }

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Popup up a number dialog for users to goto a page.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::gotoPage()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    ui::NumberDialog dlg(this);
    dlg.updateTitle(tr("跳转"));
    int ret = dlg.popup(0, m_displayControler->getPage()->iPageCount);
    refreshScreen();

    if (ret == QDialog::Rejected)
    {
        Out_Log(__FUNCTION__, COCLASSNAME);
        return;
    }
    int iPage = dlg.value() - 1;

    m_displayControler->clearPages();

    requestPage(iPage);

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Popup the setting dialog. And then update the profile.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::setting()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    if (m_settingDlg->popup())
    {
        if (!m_displayControler->isEmpty()
            && m_thread->updateProfile(m_profile->imgProfile,
                                      m_displayControler->getPage()->iPage,
                                      m_displayControler->getPage()->iDivPage))
        {
            m_displayControler->clearPages();
        }
    }

    m_profile->writeProfile();

    updateWaveform();

    bool sideWards = m_profile->imgProfile.option & Opt_SideWards;
    if (m_profile->iSideWardsDirect == 0) // left-hand
    {
        sys::SysStatus::instance().setScreenTransformation(90 * sideWards);
    }
    else // right-hand
    {
        sys::SysStatus::instance().setScreenTransformation(270 * sideWards);
    }

    setFocus();
    update();

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Slot to recieve a processed page send by the processing thread.  
 * 
 * @author reach (5/26/2010)
 * 
 * @param page: Pointer to the processed page send by the processing thread.
 */
void MainDialog::updatePage(DivPage* page)
{
    In_Log(__FUNCTION__, COCLASSNAME);
    if (page != NULL)
    {
        if (!page->processed)
        {
            page = m_thread->RequestPage(page->iPage, page->iDivPage);
        }

        if (page != NULL)
        {
            m_displayControler->addPage(page, m_isPrev);
            m_displayControler->recoverContent();
            update();
        }
    }

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Popup the GrayScaleDialog for users to setting grayscale.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::setGrayscale()
{
    In_Log(__FUNCTION__, COCLASSNAME);

    if (m_displayControler->isEmpty())
    {

        Out_Log(__FUNCTION__, COCLASSNAME);
        return; 
    }

    QImage imgSrc(*m_displayControler->getPage()->images[Cot_Scaled]);
    GrayScaleSet gs = GrayScaleDlg::setGrayScale(&imgSrc, m_profile->grayscale);

    // If the GrayScaleSet is valid, update the color table.
    if (gs.black != -1)
    {
        m_profile->grayscale = gs;
        GrayScaleDlg::setGrayTable(m_colorTable,
                                   m_profile->grayscale.black,
                                   m_profile->grayscale.middle,
                                   m_profile->grayscale.white);
    }

    Out_Log(__FUNCTION__, COCLASSNAME);
}

/**
 * Just for testing. Display the scaled image.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::OnScale()
{
    m_displayControler->restoreContent();
    m_displayControler->setContent(Cot_Scaled);
    update();
}

/**
 * Just for testing. Display the threshold image.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::OnThresh()
{
    m_displayControler->restoreContent();
    m_displayControler->setContent(Cot_Thresh);
    update();
}

/**
 * Just for testing. Display the contours image.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::OnContour()
{
    m_displayControler->restoreContent();
    m_displayControler->setContent(Cot_Contour);
    update();
}

/**
 * Just for testing. Display the lines image.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::OnLine()
{
    m_displayControler->restoreContent();
    m_displayControler->setContent(Cot_Line);
    update();
}

/**
 * Just for testing. Display the scroll lines image.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::OnScroll()
{
    m_displayControler->restoreContent();
    m_displayControler->setContent(Cot_Scroll);
    update();
}

/**
 * The slot to recieve the shudown signal send by system.
 * 
 * @author reach (5/26/2010)
 */
void MainDialog::shutdown()
{
    QApplication::exit();
}

/**
 * The slot to recieve the wakeup signal send by system.
 * 
 * @author reach (6/2/2010)
 */
void MainDialog::wakeup()
{
    if (m_profile->isZip)
    {
        m_thread->reOpenZip();
    }
}
